import express from "express"; // Импортируем в проект express.
import mongoose from "mongoose"; // Импортируем в проект mongoose - для работы с mongodb.
import { config } from "dotenv"; // Импортируем метод config из dotenv - подключает переменные среды.
import { validationResult } from "express-validator"; // Импортируем метод validationResult из express-validator - валидатор.
import { newUserValidator } from "./validations/validation-new-user.js"; // Импортируем объект newUserValidator с инструкциями для дальнейшей валидации из validation-new-user.js
import { newStudentValidator } from "./validations/validation-new-student.js"; // Импортируем объект newStudentValidator с инструкциями для дальнейшей валидации из validation-new-student.js
import { userConnection, studentConnection } from "./db.js"; // Импортируем переменные userConnection и studentConnection с объектами подключения к БД из db.js
import jwt from "jsonwebtoken"; // Импортируем в проект Json Web Token для работы с токенами аутентификации.
import StudentModel from "./models/student.js"; // Импортируем модель студента для загрузки создания документов для дальнейшей загрузки в БД.
import UserModel from "./models/user.js"; // Импортируем модель студента для загрузки создания документов для дальнейшей загрузки в БД.

import checkAuth from "./utils/checkAuth.js"; // Импортируем метод для проверки авторизации пользователя

// Подключаем переменные среды

config();

const listenPort = process.env.SERVER_PORT; // Порт для сервера
const jwtSecret = process.env.JWT_SECRET; // JWT секрет

// Вся логика express будет в переменной app
const app = express();

// Используем функцию json из самого express
app.use(express.json());

// Обработчик GET для теста доступности сервера из WAN
app.get("/", (req, res) => {
	console.log("Получен GET запрос!");
	res.status(200).send("<h1>Заработало!</h1>");
});

// Обработчик GET для теста работы middleware
// определяем методом checkAuth имеет ли пользователь права для доступа
// Если имеет ищем в БД пользователей документ данного пользователя по его Id и кладем его в переменную user. Id достаем из req.userId туда он был передан методом checkAuth
// Если пользователя с таким Id нет в БД возвращаем пользователю код 403 + json с сообщением о том что такой пользователь не зарегистрирован
// создаем документ пользователя userDoc на основе данных из БД
// Возвращаем пользователю код 200 + userToken + userData

app.get("/auth/me/", checkAuth, async (req, res) => {
	try {
		const user = await UserModel.findById(req.userId);
		if (!user) {
			return res.status(400).json({
				message: "Пользователь не зарегистрирован.",
			});
		}
		const { userPassword, ...userData } = user._doc;
		console.log("Успешная авторизация: " + userData.userEmail);
		res.status(200).json({ userData });
	} catch (errors) {
		console.log(errors);
	}
});

// Обработчик POST запросов на /auth/register
// Принимаем POST запрос от пользователя на ADDRESS:SERVER_PORT/auth/register
// Валидируем полученный json функцией validationResult кладем результат в переменную errors
// Отлавливаем ошибки через try catch
// Дальше если errors пустой, то метод isEmpty передаст true с помощью логического отрицания ! мы по итогу получим false
// Если ошибки валидации есть, то false превратится в true и тогда мы возвращаем пользователю код 400 + json информацию об ошибке
// Если ошибок валидации нет, то создаем документ пользователя userDoc на основе модели UserModel
// Если ловим ошибку записи в БД, то возвращаем пользователю код 400 + json с сообщением о том что не удалось зарегистрироваться
// Если документ успешно записан в БД, то генерируем JWT токен авторизации userToken
// Реструктурируем userDoc и создаем объект пользователя только без графы пароля userData
// Возвращаем пользователю код 200 + userToken + userData
// Выводим в консоль сообщение о том что новый пользователь зарегистрирован + userData.userEmail

app.post("/auth/register", newUserValidator, async (req, res) => {
	const errors = validationResult(req);
	try {
		if (!errors.isEmpty()) {
			return res.status(400).json(errors.array());
		}
		const userDoc = new UserModel({
			userEmail: req.body.userEmail,
			userRealName: req.body.userRealName,
			userPassword: req.body.userPassword,
			userAvatarURL: req.body.userAvatarURL,
		});
		const user = await userDoc.save();
		const token = jwt.sign(
			{
				_id: user._id,
			},
			jwtSecret,
			{
				expiresIn: "7d",
			}
		);
		const { userPassword, ...userData } = user._doc;
		console.log("Успешная регистрация: " + userData.userEmail);
		res.status(200).json({ userData, token });
	} catch (err) {
		console.log(err);
		res.status(400).json({
			message: "Не удалось зарегистрироваться.",
		});
	}
});

// Принимаем POST запрос от пользователя на localhost:SERVER_PORT/auth/login
// Выполняем поиск пользователя с таким _userEmail_ в БД пользователей.
// Если пользователь найден, то проверяем совпадает ли переданный пользователем userPassword с userPassword из БД.
// Если userPassword совпадает, то то генерируем JWT токен авторизации userToken.
// Возвращаем пользователю код 200 + userToken + userData

app.post("/auth/login", newUserValidator, async (req, res) => {
	try {
		const user = await UserModel.findOne({ userEmail: req.body.userEmail });
		if (!user) {
			return res.status(400).json({
				message: "Пользователь не зарегистрирован.",
			});
		}
		if (user.userPassword === req.body.userPassword) {
		} else {
			return res.status(400).json({
				message: "Неверный пароль.",
			});
		}
		const token = jwt.sign(
			{
				_id: user._id,
			},
			jwtSecret,
			{
				expiresIn: "7d",
			}
		);
		const { userPassword, ...userData } = user._doc;
		console.log("Успешная авторизация: " + userData.userEmail);
		res.status(200).json({ userData, token });
	} catch (err) {
		console.log(err);
		res.status(500).json({ message: "Ошибка авторизации." });
	}
});

// Принимаем POST запрос от пользователя на localhost:SERVER_PORT/student/new
// Валидируем полученные данные.
// Если валидация проходит успешно тогда создаем документ нового студена studentDoc на основе модели StudentModel.
// Загружаем данные в базу данных асинхронной функцией.

app.post("/students/new", newStudentValidator, async (req, res) => {
	const errors = validationResult(req);
	try {
		if (!errors.isEmpty()) {
			return res.status(400).json(errors.array());
		}
		const studentDoc = new StudentModel({
			studentRealName: req.body.studentRealName,
			studentEnglishName: req.body.studentEnglishName,
			studentBirthDate: req.body.studentBirthDate,
			studentPhotoURL: req.body.studentPhotoURL,
		});
		const student = await studentDoc.save();
		console.log("Добавлен новый студент: " + studentDoc.studentRealName + " | " + studentDoc.studentEnglishName);
		res.status(200).json({ studentDoc });
	} catch (err) {
		console.log(err);
		res.status(400).json({
			message: "Не ужалось записать нового студента.",
		});
	}
});

// Запускаем сервер
app.listen(listenPort, (err) => {
	if (err) {
		return console.log(err);
	}
	console.log("Server OK");
});
